﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Reflection;

namespace ROrganizer.UI.Controllers.Core
{
    public class AsyncWorker
    {
        private readonly Action notifier;
        private readonly Action aborter;
        private readonly Action<Exception> exceptioner;

        private Thread thread;
        private bool isWorking;

        public AsyncWorker(Action notifier, Action aborter = null, Action<Exception> exceptioner = null)
        {
            this.notifier = notifier;
            this.aborter = aborter ?? (() => { });
            this.exceptioner = exceptioner ?? (ex => { });
        }

        public bool IsWorking
        {
            get { return this.isWorking; }
        }

        public bool CanStop
        {
            get { return this.thread != null; }
        }

        public void Stop()
        {
            lock (this)
            {
                if (this.thread != null)
                    this.thread.Abort();
            }
        }

        public void Execute(Action action, Action aborter = null, Action<Exception> exceptioner = null)
        {
            this.ExecuteInternal(action, aborter, exceptioner);
        }

        public void Execute<T>(Action<T> action, T param1, Action aborter = null, Action<Exception> exceptioner = null)
        {
            this.ExecuteInternal(() => action(param1), aborter, exceptioner);
        }

        private void ExecuteInternal(Action action, Action aborter, Action<Exception> exceptioner)
        {
            lock (this)
            {
                ThreadPool.QueueUserWorkItem(this.ThreadCallback, new ThreadStarter { Callback = action, Aborter = aborter ?? this.aborter, Exceptioner = exceptioner ?? this.exceptioner });
                this.isWorking = true;
                this.notifier();
            }
        }

        private void ThreadCallback(object state)
        {
            var starter = state as ThreadStarter;

            lock (this)
            {
                this.thread = Thread.CurrentThread;
                this.notifier();
            }

            try
            {
                starter.Callback();
            }
            catch (ThreadAbortException)
            {
                starter.Aborter();
            }
            catch (AggregateException ex)
            {
                starter.Exceptioner(ex.InnerException);
            }
            catch (TargetInvocationException ex)
            {
                starter.Exceptioner(ex.InnerException);
            }
            catch (Exception ex)
            {
                starter.Exceptioner(ex);
            }
            finally
            {
                lock (this)
                {
                    this.thread = null;
                    this.isWorking = false;
                    this.notifier();
                }
            }
        }

        private class ThreadStarter
        {
            public Action Callback { get; set; }
            public Action Aborter { get; set; }
            public Action<Exception> Exceptioner { get; set; }
        }
    }
}
